package com.activiti.demo.demos.flow.component;

import com.activiti.demo.demos.flow.entity.ProcessFormDetail;
import com.activiti.demo.demos.flow.mapper.ProcessFormDetailMapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.engine.HistoryService;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.activiti.image.impl.DefaultProcessDiagramGenerator;
import org.springframework.stereotype.Component;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;

/**
 * @Description: Act组件
 * @Author: LuanKay
 * @Date: 2025/2/17
 */
@Slf4j
@Component
@AllArgsConstructor
public class ActComponent {
    private final RepositoryService repositoryService;
    private final RuntimeService runtimeService;
    private final TaskService taskService;
    private final HistoryService historyService;
    private final ProcessFormDetailMapper formDetailMapper;

    /**
     * 部署流程
     *
     * @param bpmnStream 流程文件流
     */
    public String deploy(InputStream bpmnStream) {
        try {
            // 创建并部署流程
            Deployment deployment = repositoryService.createDeployment()
                    .name("请假流程")
                    .addInputStream("demo.bpmn20.xml", bpmnStream)
                    .name(new Date().toString())
                    .deploy();
            log.info("部署流程成功，ID:{}", deployment.getId());
            return deployment.getId();
        } catch (Exception e) {
            log.error("部署流程失败", e);
        }
        return null;
    }

    /**
     * 发起流程实例
     *
     * @param deployId 流程部署id
     */
    public String startProcess(String deployId, String name) {
        try {
            ProcessInstance processInstance = runtimeService.createProcessInstanceBuilder()
                    .processDefinitionId(deployId)
                    .name(name)
                    .start();
            if (Objects.isNull(processInstance)) {
                log.warn("未找到对应的部署ID，无法启动流程实例: {}", deployId);
            }
            log.info("流程实例ID:{}", processInstance.getProcessInstanceId());

            return processInstance.getId();
        } catch (Exception e) {
            log.error("启动流程实例失败，部署ID: {}", deployId, e);
        }
        return null;
    }

    /**
     * 获取未完成节点
     *
     * @param processId 流程实例id
     */
    public void getUnfinishedNodes(String processId) {
        List<Task> list = taskService.createTaskQuery()
                .processInstanceId(processId)
                // 获取当前已分配的任务
                .active()
                .list();
        list.forEach(task -> log.info("未完成节点——: {}", task.getName()));
    }

    /**
     * 提交任务
     *
     * @param processId 流程实例id
     * @param variables 变量
     */
    public void completeTask(String processId, String user, Map<String, Object> variables) {
        try {
            // 获取任务实例
            Task task = taskService.createTaskQuery()
                    .processInstanceId(processId)
                    .active()
                    .singleResult();
            if (Objects.isNull(task)) {
                log.warn("未找到对应的活跃任务，流程实例ID: {}", processId);
                return;
            }
            String taskId = task.getId();
            // 提交人校验
//            if (checkUser(processId, user, variables, task)) {
//                return;
//            }

            // 表单校验
//            if (checkForm(variables, taskId)) {
//                return;
//            }

            // 获取任务id并完成任务
            taskService.complete(taskId, variables);
            log.info("完成任务，流程实例ID: {}, 任务: {}", processId, task.getName());
        } catch (Exception e) {
            log.error("完成任务失败，流程实例ID: {}", processId, e);
        }
    }

    /**
     * 校验提交人
     *
     * @param processId 流程实例id
     * @param user      提交人
     * @param variables 变量
     * @param task      任务实例
     */
    private static boolean checkUser(String processId, String user, Map<String, Object> variables, Task task) {
        if (!task.getAssignee().equals(user)) {
            log.warn("当前提交人与流程设置的提交人不一致，流程实例ID: {}, 任务名称: {}", processId, task.getName());
            return true;
        }
        variables.put(task.getName() + ":提交人", user);
        return false;
    }

    /**
     * 表单校验
     *
     * @param variables 变量
     * @param taskId    任务id
     */
    private boolean checkForm(Map<String, Object> variables, String taskId) {
        List<ProcessFormDetail> formList = getFormByTaskId(taskId);
        // 表单校验
        List<String> keyList = new ArrayList<>(variables.keySet());
        for (ProcessFormDetail e : formList) {
            if (!keyList.contains(e.getFieldName())) {
                log.warn("请提交完整数据，字段缺失: {}", e.getFieldName());
                return true;
            }
            // todo 业务判断
            if (1 == e.getIsRequired() && Objects.isNull(variables.get(e.getFieldName()))) {
                log.warn("请提交完整数据，字段必填: {}", e.getFieldName());
                return true;
            }
        }
        return false;
    }

    /**
     * 获取当前流程的表单
     *
     * @param taskId 任务id
     */
    public List<ProcessFormDetail> getFormByTaskId(String taskId) {
        // 当前节点表单key
        String formKey = taskService.createTaskQuery().taskId(taskId).singleResult().getFormKey();
        // 去业务表查表单格式
        // todo:省略查询父表单代码
        return formDetailMapper.selectList(new LambdaQueryWrapper<ProcessFormDetail>()
                .eq(ProcessFormDetail::getFormId, formKey));
    }

    /**
     * 获取流程实例图
     *
     * @param processId 流程实例id
     */
    public String getProcessDiagramBas64(String processId) throws IOException {
        // 获取流程实例的定义
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
                .processInstanceId(processId)
                .singleResult();

        // 获取流程定义ID
        String processDefinitionId = processInstance.getProcessDefinitionId();

        // 获取BPMN模型文件流
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);

        // 获取当前活动节点id(当前正在运行的任务节点)
        List<String> activeActivityIds = runtimeService.getActiveActivityIds(processId);

        // 历史活动线
        List<String> flowActivityIds = new ArrayList<>();
        List<HistoricActivityInstance> historicSequenceFlow = historyService.createHistoricActivityInstanceQuery()
                .processInstanceId(processId)
                .activityType("sequenceFlow")
                .list();
        historicSequenceFlow.forEach(historicActivityInstance -> flowActivityIds.add(historicActivityInstance.getActivityId()));

        // 生成流程图
        String fontName = "宋体";
        DefaultProcessDiagramGenerator diagramGenerator = new DefaultProcessDiagramGenerator();
        InputStream inputStream = diagramGenerator.generateDiagram(
                bpmnModel,
                activeActivityIds,
                flowActivityIds,
                fontName,
                fontName,
                fontName,
                false
        );

        // 转换为base64字符串
        ByteArrayOutputStream outStream = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024];
        int read = inputStream.read(buffer);

        while (read != -1) {
            outStream.write(buffer, 0, read);
            read = inputStream.read(buffer);
        }
        inputStream.close();
        byte[] data = outStream.toByteArray();
        return Base64.getEncoder().encodeToString(data);
    }


}
